package pl.edu.agh.gronoMining.frequentSubgraphFinder.parsers.graph;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.util.Set;

import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.Edge;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.Graph;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.GraphException;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.Node;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.simpleGraph.SimpleEdge;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.simpleGraph.SimpleGraph;
import pl.edu.agh.gronoMining.frequentSubgraphFinder.graph.simpleGraph.SimpleNode;

/**
 * Parser that uses 'line' graph representation. This representation contains three type of grah:
 * <br />
 * <code>t # {id}</code>  // start of new graph, id of graph is {id} [integer] <br />
 * <code>v {id1} {type}:{label}</code>  // declaration of node. Node has id ({id}, integer), type ({type}, integer),
 * and label ({label}, integer) <br />
 * <code>e {id1} {id2} {label}</code> // declaration of edge that connects nodes with {id1} and {id2}. Edge has label {Label} [integer]
 * <br /><br />
 * Example of simple graph (cycle with three nodes)<br />
 * <code>
 * t # 1<br />
 * v 0 1:11<br />
 * v 1 2:22<br />
 * v 2 1:33<br />
 * e 0 1 101<br />
 * e 1 2 102<br />
 * e 2 1 103<br />
 * </code>
 */
public class ExtendedLineGraphParser implements GraphParser {

	public Graph parse(File graphFile) throws ParseException {
		
		Graph graph = new SimpleGraph();
		
		int lineNo = 1;
		
		try {
			
			BufferedReader br = new BufferedReader(new FileReader(graphFile));
			
			String line;
			boolean graphDefined = false;
			
			while ((line = br.readLine()) != null) {
				line = line.trim();
				if (! line.equals("")) {
					
					String[] lineData = line.split(" ");
					
					String action = lineData[0].trim();
					
					if ("t".equals(action)) {
						
						if (graphDefined) {
							throw new ParseException("You can only specify on graph", lineNo);
						} else {
							if (lineData.length != 3) {
								throw new ParseException("unknown command: '" + line + "'", lineNo);
							} else {
								String hash = lineData[1].trim();
								if (! "#".equals(hash)) {
									throw new ParseException("unknown command: '" + line + "'", lineNo);
								} else {
									//String idString = lineData[2].trim();
									//int id = Integer.parseInt(idString);
									
									graphDefined = true;
								}
							}
						}
						
					} else if ("v".equals(action)) {
						
						if (lineData.length != 3) {
							throw new ParseException("unknown command: '" + line + "'", lineNo);
						} else {
							String idString = lineData[1].trim();
							String valueString = lineData[2].trim();
							int id = Integer.parseInt(idString);
							String[] values =  valueString.split(":");
							if (values.length != 2) {
								throw new ParseException("wrong label and type: '" + line + "'", lineNo);
							} else {
								int type = Integer.parseInt(values[0].trim());
								int label = Integer.parseInt(values[1].trim());
								Node node = new SimpleNode(id, label, type);
								graph.addNode(node);
							}
						}
						
					} else if ("e".equals(action)) {
						
						if (lineData.length != 4) {
							throw new ParseException("unknown command: '" + line + "'", lineNo);
						} else {
							
							String nodeAidString = lineData[1].trim();
							String nodeBidString = lineData[2].trim();
							String labelString = lineData[3].trim();
							
							int nodeAid = Integer.parseInt(nodeAidString);
							int nodeBid = Integer.parseInt(nodeBidString);
							int label = Integer.parseInt(labelString);
							
							Node nodeA = graph.getNodeById(nodeAid);
							Node nodeB = graph.getNodeById(nodeBid);
							
							if (nodeA == null) {
								throw new ParseException("unknown node: '" + nodeA + "'", lineNo);
							} else if (nodeB == null) {
								throw new ParseException("unknown node: '" + nodeB + "'", lineNo);
							} else {
								Edge edge = new SimpleEdge(label, nodeA, nodeB);
								edge.checkIntegraty();
							}
						}
						
					} else {
						throw new ParseException("unknown character '"+action+"'", lineNo);
					}
					
				}
				lineNo++;
			}
			
		} catch (IOException e) {
			throw new ParseException("No such file: "+graphFile.getAbsolutePath(), lineNo);
		} catch (NumberFormatException e) {
			throw new ParseException("value is not integer", lineNo);
		} catch (GraphException e) {
			throw new ParseException(e.getMessage(), lineNo);
		}
		
		return graph;	
	}
	
	public void serializeToFile(Set<Graph> graphsSet, File outputFile) {
		
		try {
			int graphId = 1;
			
			outputFile.createNewFile();
			PrintWriter writer = new PrintWriter(new FileWriter(outputFile));
			
			for (Graph graph : graphsSet) {
			
				writer.println("t # "+graphId);
				
				for (Node node : graph.getAllNodes()) {
					//writer.println("v "+node.getId()+" "+node.getType()+":"+node.getLabel());
					writer.println("v "+node.getId()+" "+node.getLabel());
				}
				
				for (Node node : graph.getAllNodes()) {
					for (Edge edge : node.getEdges()) {
						Node otherNode = edge.getOtherNode(node);
						if (node.getId() < otherNode.getId())
							writer.println("e "+node.getId()+" "+otherNode.getId()+" "+edge.getLabel());
					}
				}
				
				writer.println();
				
				graphId++;
			}
			
			writer.flush();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
}
